package com.foxchen.offline.task.service.impl;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.foxchen.offline.task.TaskHandlerFactory;
import com.foxchen.offline.task.bean.ResponseBean;
import com.foxchen.offline.task.bean.TaskPojo;
import com.foxchen.offline.task.config.OfflineTaskConfig;
import com.foxchen.offline.task.dto.TaskInfoDto;
import com.foxchen.offline.task.enums.OfflineTaskStatusEnum;
import com.foxchen.offline.task.mapper.OfflineTaskMapper;
import com.foxchen.offline.task.service.BaseInfoService;
import com.foxchen.offline.task.service.OfflineTaskService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.PageInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.UUID;


@Service
@Slf4j
public class OfflineTaskServiceImpl implements OfflineTaskService {
    @Resource
    @Lazy
    private TaskHandlerFactory taskHandlerFactory;
    @Resource
    private DataSource dataSource;
    @Resource
    private OfflineTaskConfig offlineTaskConfig;
    @Autowired(required = false)
    private BaseInfoService baseInfoService;
    @Resource
    private PageInterceptor pageInterceptor;
    private SqlSessionFactory sqlSessionFactory;

    private static final String DEFAULT_USER = "system";

    @PostConstruct
    public void setUp() throws Exception{
        // 初始化mybatis相关
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);

        String locationPattern = "classpath*:sql/mapper/*.xml";
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        Configuration configuration = new Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.addInterceptor(pageInterceptor);
        sessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactory = sessionFactoryBean.getObject();
        log.info("sqlSessionFactory create ok.continue...");

        // 校验是否有实现
        if(null == baseInfoService){
            log.warn("when use offline task service,suggestion define a bean type of BaseInfoService,now some info be default.");
        }
    }

    @Override
    public ResponseBean<String> createTask(TaskInfoDto taskInfo) {
        // 新建任务
        TaskPojo taskPo = TaskPojo
                .builder()
                .id(UUID.randomUUID().toString())
                .name(taskInfo.getName())
                .status(OfflineTaskStatusEnum.NEW.getStatus())
                .type(taskInfo.getType())
                .fileName(taskInfo.getFileName())
                .fileSuffix(taskInfo.getFileSuffix())
                .module(offlineTaskConfig.getModuleName())
                .param(taskInfo.getTaskParam())
                .createUserId(baseInfoService == null ? DEFAULT_USER : baseInfoService.getCreateUserId())
                .gmtCreate(new Date())
                .build();

        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            OfflineTaskMapper taskMapper = sqlSession.getMapper(OfflineTaskMapper.class);
            int insert = taskMapper.insert(taskPo);
            log.info("task={}",taskPo);
            // 交给线程池执行任务
            taskHandlerFactory.executeTask(taskPo);

            return ResponseBean.success(String.valueOf(insert));
        }
    }

    @Override
    public ResponseBean<String> deleteTask(String taskId) {
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            OfflineTaskMapper taskMapper = sqlSession.getMapper(OfflineTaskMapper.class);
            int insert = taskMapper.deleteById(taskId);

            // 交给线程池停止任务
            taskHandlerFactory.stopTaskByTaskId(taskId);
            return ResponseBean.success(String.valueOf(insert));
        }
    }

    @Override
    public Integer updateTask(TaskPojo taskPojo) {
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            OfflineTaskMapper taskMapper = sqlSession.getMapper(OfflineTaskMapper.class);
            return taskMapper.updateById(taskPojo);
        }
    }

    @Override
    public PageInfo<TaskPojo> listTask(TaskInfoDto taskInfo,Integer page,Integer pageSize) {
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            OfflineTaskMapper taskMapper = sqlSession.getMapper(OfflineTaskMapper.class);
            try (Page<Object> objectPage = PageHelper.startPage(page, pageSize)) {
                List<TaskPojo> dataList = taskMapper.list(taskInfo);
                PageInfo<TaskPojo> retData = new PageInfo<>(dataList);
                log.debug("total={},{}",objectPage.getTotal(),retData.getTotal());
                return retData;
            }
        }
    }

    @Override
    public List<TaskPojo> selectUnBeginTask() {
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            OfflineTaskMapper taskMapper = sqlSession.getMapper(OfflineTaskMapper.class);
            return taskMapper.selectUnBeginTask();
        }
    }

    @Override
    public void downloadTask(String id, HttpServletResponse response) throws Exception{
        // 校验任务状态
        try(SqlSession sqlSession = sqlSessionFactory.openSession()){
            OfflineTaskMapper taskMapper = sqlSession.getMapper(OfflineTaskMapper.class);
            TaskPojo taskPojo = taskMapper.getById(id);
            if(!OfflineTaskStatusEnum.SUCCESS.getStatus().equals(taskPojo.getStatus())){
                writeErrorMsg(response,"任务非完成状态无法下载!");
                return;
            }
            String filePath = offlineTaskConfig.getExportPath() + File.separator + taskPojo.getFilePath();
            boolean exists = new File(filePath).exists();
            if(!exists){
                writeErrorMsg(response,"该任务文件已不存在!");
                return;
            }

            // 真实文件后缀
            String fileSuffix = "." + filePath.split("\\.")[1];

            // 读文件 往输出流中写
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            String encodeFileName = URLEncoder.encode(taskPojo.getFileName(), StandardCharsets.UTF_8.toString());
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodeFileName + fileSuffix);

            response.getOutputStream().write(Files.readAllBytes(Paths.get(filePath)));
        } catch (Exception e){
            log.error("downloadTask error:{}",id,e);
            throw e;
        }
    }

    private void writeErrorMsg(HttpServletResponse response, String msg) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        ResponseBean<String> responseBean = ResponseBean.error(msg);
        response.getWriter().write(new ObjectMapper().writeValueAsString(responseBean));
    }

}
